/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *   gbx-emu - breakpointswidget.cpp                                       *
 *   gbx-emu homepage: http://code.google.com/p/gbx-emu/                   *
 *   Copyright (C) 2009 olejl77@gmail.com                                  *
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the          *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "breakpointswidget.h"
#include "ui_breakpointswidget.h"
#include "debuggerwidget.h"
#include "mainwindow.h"
#include "../core/gameboy.h"
#include "../core/debugger.h"
#include <QInputDialog>
#include <QMessageBox>

BreakpointsWidget::BreakpointsWidget(QWidget *parent) :
    QWidget(parent), ui(new Ui::BreakpointsWidgetClass)
{
    _dbg = NULL;
    _dbgWidget = NULL;
    ui->setupUi(this);

    // signals/slots mechanism in action
    connect( ui->pushAdd,     SIGNAL( clicked() ), this, SLOT( add()     ));
    connect( ui->pushRemove,  SIGNAL( clicked() ), this, SLOT( remove()  ));
    connect( ui->pushEnable,  SIGNAL( clicked() ), this, SLOT( enable()  ));
    connect( ui->pushDisable, SIGNAL( clicked() ), this, SLOT( disable() ));
    connect( ui->pushToggle,  SIGNAL( clicked() ), this, SLOT( toggle()  ));
    connect( ui->pushEdit,    SIGNAL( clicked() ), this, SLOT( edit()    ));

    model = new QStringListModel();
    ui->listView->setModel(model);
}

BreakpointsWidget::~BreakpointsWidget()
{
    delete ui;
    _dbg = NULL;
    _dbgWidget = NULL;
    ui = NULL;
}

void BreakpointsWidget::update()
{
    updateBreakpoints();
}

void BreakpointsWidget::updateBreakpoints()
{
    QStringList list;

    u32 num = _dbg->getNumBreakpoint();

    for(u32 row = 0; row < num; row++ ) {
        list << get_breakpoint_display_string(row);
        _dbgWidget->updateBP(_dbg->bpArray[row].address);
    }
    model->setStringList(list);
}


void BreakpointsWidget::changeEvent(QEvent *e)
{
    switch (e->type()) {
    case QEvent::LanguageChange:
        ui->retranslateUi(this);
        break;
    default:
        break;
    }
}

void BreakpointsWidget::add()
{
    bool ok;
    QString text = QInputDialog::getText(this, tr("Breakpoint"),
                                      tr("Value:"), QLineEdit::Normal, "", &ok);
    if (ok) {
        parseBreakpoint(text);
        updateBreakpoints();
    }
}

void BreakpointsWidget::remove()
{
    const QModelIndex& index = ui->listView->selectionModel()->currentIndex();

    if (index.isValid()) {
        // TODO
        updateBreakpoints();
    }
}

void BreakpointsWidget::enable()
{
    ToggleBreakpoint(Enable);
}

void BreakpointsWidget::disable()
{
    ToggleBreakpoint(Disable);
}

void BreakpointsWidget::toggle()
{
    ToggleBreakpoint(Toggle);
}

void BreakpointsWidget::edit()
{
    /*
    const QModelIndex& index = listView->selectionModel()->currentIndex();
    QString old;
    bool ok;

    if (index.isValid()) {
        old = model->data(index,Qt::DisplayRole).toString();
        old.remove(0,4);
        QString text = QInputDialog::getText(this, tr("Edit Breakpoint"),
                                          tr("New Value:"), QLineEdit::Normal, old, &ok);
        if (ok) {
            breakpoint_parse(text, index.row());
            update_breakpoint();
        }
    }
    */
}

QString BreakpointsWidget::get_breakpoint_display_string(int row)
{
    QString str;
    QString address;
    u32 flags = _dbg->bpArray[row].flags;
    u16 addr = _dbg->bpArray[row].address;

    QString flagR = (flags & BPT_FLAG_READ) ? "R" : "_";
    QString flagW = (flags & BPT_FLAG_WRITE) ? "W" : "_";
    QString flagX = (flags & BPT_FLAG_EXEC) ? "X" : "_";
    QString flagL = (flags & BPT_FLAG_LOG) ? "L" : "_";
    QString status = (flags & BPT_FLAG_ENABLED) ? "e" : "d";

    address = QString("0x%1").arg(addr, 4, 16, QChar('0')).toUpper();
    str = QString("%1 / %2%3%4%5 %6").arg(status).arg(flagR).arg(flagW).arg(flagX).arg(flagL).arg(address);
    return str;
}

void BreakpointsWidget::ToggleBreakpoint(int flag)
{
    QModelIndex index = ui->listView->selectionModel()->currentIndex();
    int i = index.row();
    if (flag == Enable) {
        _dbg->enableBreakpoint(i);
    } else if (flag == Disable) {
        _dbg->disableBreakpoint(i);
//    } else if (BPT_CHECK_FLAG(debugger::g_Breakpoints[i], BPT_FLAG_ENABLED)) {
//        dbg->main->_gb.bp->disableBreakpoint(i);
    } else {
        _dbg->enableBreakpoint(i);
    }
    updateBreakpoints();
}

void BreakpointsWidget::parseBreakpoint(QString text, int row)
{
    breakpoint bp;
    //Enabled by default
    bp.flags = BPT_FLAG_ENABLED;
    bp.address = 0;
    bool ok;

    if (text.contains("*", Qt::CaseInsensitive)) {
        bp.flags &= ~BPT_FLAG_ENABLED;
    } else if(text.contains("r", Qt::CaseInsensitive)) {
        bp.flags |= BPT_FLAG_READ;
    } else if(text.contains("w", Qt::CaseInsensitive)) {
        bp.flags |= BPT_FLAG_WRITE;
    } else if(text.contains("x", Qt::CaseInsensitive)) {
        bp.flags |= BPT_FLAG_EXEC;
    } else if(text.contains("l", Qt::CaseInsensitive)) {
        bp.flags |= BPT_FLAG_LOG;
    }

    //If none of r/w/x specified, default to exec
//    if(!(bp.flags & (BPT_FLAG_EXEC | BPT_FLAG_READ | BPT_FLAG_WRITE))) {
//        BPT_SET_FLAG(bp, BPT_FLAG_EXEC);
//    }

    int first = text.indexOf(QRegExp("[0123456789abcdefABCDEF]"));
    text.remove(0,first);
    int last = text.lastIndexOf(QRegExp("[0123456789abcdefABCDEF]"));
    text.truncate(last+1);

    QStringList parts = QString(text).split(" ", QString::SkipEmptyParts);
    if (parts.size() == 1) {
        bp.address = parts.at(0).toULong( &ok, 16 );
    } else if (parts.size() == 2) {
        bp.address = parts.at(0).toULong( &ok, 16 );
    } else {
        ok = false;
    }

    if (ok) {
        if (row < 0) {
            if(!_dbg->addBreakpointStruct(&bp)) {
                QMessageBox::warning(this, tr("Warning"),
                        tr("Cannot add any more breakpoints."),
                        QMessageBox::Ok);
            }
        } else {
            //debugger::replace_breakpoint_num(row, &bp);
        }
    }
}
